package jscinttools.datafit;
import jscinttools.utilities.aidautilities.AidaFactories;
import jscinttools.datafit.scint.PulseFuncFactory;
import jscinttools.datafit.scint.SpecFuncFactory;
import jscinttools.datafit.scint.PulseFitFunc;
import jscinttools.datafit.scint.SpectrumFitFunc;
import hep.aida.IFunction;
import hep.aida.IFunctionFactory;
import hep.aida.ITree;
import java.lang.StringBuilder;
import java.lang.System;

/**
 *  Description of the Class
 *
 *@author     Marc-Antoine Verdier
 *@created    14 juin 2011
 */
public class FitFuncFactory{
	
	/**  Constructor for the FitFuncFactory object */
	private FitFuncFactory() { }

	/**
	 *  Create a factory for pulse fit functions
	 *
	 *@param  tree  The ITree on which IFunction can be hooked
	 *@return       PulseFuncFactory
	 */
	public static PulseFuncFactory pulseFuncFactory(ITree tree){
		return new PulseFuncFactoryImp(tree);
	}

	/**
	 *  Create a factory for pulse fit functions
	 *
	 *@return    PulseFuncFactory
	 */
	public static PulseFuncFactory pulseFuncFactory(){
		return pulseFuncFactory(AidaFactories.TREE);
	}

	/**
	 *  Create a factory for spectra fit functions
	 *
	 *@param  tree  The ITree on which IFunction can be hooked
	 *@return       SpecFuncFactory
	 */
	public static SpecFuncFactory specFuncFactory(ITree tree){
		return new SpecFuncFactoryImp(tree);
	}

	/**
	 *  Create a factory for spectra fit functions
	 *
	 *@return    SpecFuncFactory
	 */
	public static SpecFuncFactory specFuncFactory(){
		return specFuncFactory(AidaFactories.TREE);
	}
}

/**
 *  Description of the Class
 *
 *@author     Marc-Antoine Verdier
 *@created    14 juin 2011
 */
class PulseFuncFactoryImp implements PulseFuncFactory{

	private ITree tree;
	private IFunctionFactory ff;

	PulseFuncFactoryImp(ITree tree){
		this.tree = tree;
		this.ff = AidaFactories.ANALYSIS_FACTORY.createFunctionFactory(tree);
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExp(){
		class NExpImp implements PulseFitFunc{
			/**
			 *  Setup the FitFunction as A0exp(-t/tau0)+A1exp(-t/tau1)+... and the
			 *  parameters in this order: A0,tau0,A1,tau1,...
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = param.length / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != (iter - 1))
						sbF.append("A" + i + "*exp(-1.0*x[0]/tau" + i + ")+");
					else
						sbF.append("A" + i + "*exp(-1.0*x[0]/tau" + i + ")");
				}
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i + ",");
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				return sbP.toString();
			}
		}
		return new NExpImp();
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExpBck(){
		class NExpBckImp implements PulseFitFunc{
			/**
			 *  Setup the FitFunction as A0exp(-t/tau0)+A1exp(-t/tau1)+...+P0 and the
			 *  parameters in this order: A0,tau0,A1,tau1,...+P0
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 1) / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != (iter - 1))
						sbF.append("A" + i + "*exp(-1.0*x[0]/tau" + i + ")+");
					else
						sbF.append("A" + i + "*exp(-1.0*x[0]/tau" + i + ")");
				}
				sbF.append("+P0");
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i + ",");
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				sbP.append(",P0");
				return sbP.toString();
			}
		}
		return new NExpBckImp();
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExpRise(){
		class NExpRiseImp implements PulseFitFunc{
			/**
			 *  Setup the FitFunction as A0(exp(-t/tau0)-exp(-t/tauR))+A1exp(-t/tau1)+...
			 *  and the parameters in this order: A0,tau0,A1,tau1,...,tauR
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 1) / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbF.append("A" + i + "*(exp(-1.0*x[0]/tau" + i + ")-exp(-1.0*x[0]/tauR))");
					else
						sbF.append("+A" + i + "*exp(-1.0*x[0]/tau" + i + ")");
				}
				return sbF.toString();
			}
			
			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i);
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				sbP.append(",tauR");
				return sbP.toString();
			}
		}
		return new NExpRiseImp();
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExpDelayRise(){
		class NExpDelayRiseImp implements PulseFitFunc{
			/**
			 *  Setup the FitFunction as A0(exp(-(t-t0)/tau0)-exp(-(t-t0))/tauR))+A1exp(-(t-t0)/tau1)+...
			 *  and the parameters in this order: A0,tau0,A1,tau1,...,t0,tauR
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 3) / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbF.append("A" + i + "*(exp(-1.0*(x[0]-t0)/tau" + i + ")-exp(-1.0*(x[0]-t0)/tauR))");
					else
						sbF.append("+A" + i + "*exp(-1.0*(x[0]-t0)/tau" + i + ")");
				}
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i);
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				sbP.append(",t0,tauR");
				return sbP.toString();
			}
		}
		return new NExpDelayRiseImp();
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExpRiseBck(){
		class NExpRiseBckImp implements PulseFitFunc{	
			/**
			 *  Setup the FitFunction as A0(exp(-t/tau0)-exp(-t/tauR))+A1exp(-t/tau1)+...+P0
			 *  and the parameters in this order: A0,tau0,A1,tau1,...,tauR,P0
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 2) / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbF.append("A" + i + "*(exp(-1.0*x[0]/tau" + i + ")-exp(-1.0*x[0]/tauR))");
					else
						sbF.append("+A" + i + "*exp(-1.0*x[0]/tau" + i + ")");
				}
				sbF.append("+P0");
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i);
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				sbP.append(",tauR,P0");
				return sbP.toString();
			}
		}
		return new NExpRiseBckImp();
	}

	/**
	 *  Description of the Method
	 *
	 *@return    Description of the Return Value
	 */
	public PulseFitFunc nExpDelayRiseBck(){
		class NExpDelayRiseBckImp implements PulseFitFunc{
			/**
			 *  Setup the FitFunction as A0(exp(-(t-t0)/tau0)-exp(-(t-t0))/tauR))+A1exp(-(t-t0)/tau1)+...+P0
			 *  and the parameters in this order: A0,tau0,A1,tau1,...,t0,tauR,P0
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 3) / 2;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("PulseFitFunc", 1, funcS, parS, "PulseFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbF.append("A" + i + "*(exp(-1.0*(x[0]-t0)/tau" + i + ")-exp(-1.0*(x[0]-t0)/tauR))");
					else
						sbF.append("+A" + i + "*exp(-1.0*(x[0]-t0)/tau" + i + ")");
				}
				sbF.append("+P0");
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i == 0)
						sbP.append("A" + i + ",tau" + i);
					else
						sbP.append(",A" + i + ",tau" + i);
				}
				sbP.append(",t0,tauR,P0");
				return sbP.toString();
			}
		}
		return new NExpDelayRiseBckImp();
	}
}

/**
 *  Description of the Class
 *
 *@author     Marc-Antoine Verdier
 *@created    15 juin 2011
 */
class SpecFuncFactoryImp implements SpecFuncFactory{

	private ITree tree;
	private IFunctionFactory ff;

	SpecFuncFactoryImp(ITree tree){
		this.tree = tree;
		this.ff = AidaFactories.ANALYSIS_FACTORY.createFunctionFactory(tree);
	}

	/**
	 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
	 *  ...and the parameters in this order:
	 *  Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...
	 *
	 *@return    SpectrumFitFunc
	 */
	public SpectrumFitFunc nGauss(){
		class NGaussImp implements SpectrumFitFunc{	
			/**
			 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
			 *  ... and the parameters in this order:
			 *  Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length) / 3;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("SpectrumFitFunc", 1, funcS, parS, "SpectrumFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != 0)
						sbF.append("+");
					sbF.append("Amp_g" + i + "*exp(-0.5*pow((x[0]-mu_g" + i + ")/(sig_g" + i + "),2))");
				}
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != 0)
						sbP.append(",");
					sbP.append("Amp_g" + i + ",mu_g" + i + ",sig_g" + i);
				}
				return sbP.toString();
			}
		}
		return new NGaussImp();
	}

	/**
	 *  Setup the FitFunction as Amp_g*exp(-((N-mu_g)/(2*sig_g))^2)+Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m))))
	 *  and the parameters in this order: Amp_g,mu_g,sig_g,Amp_m,mu_m,sig_m
	 *
	 *@return    SpectrumFitFunc
	 */
	public SpectrumFitFunc gaussMoyal(){
		class GaussMoyalImp implements SpectrumFitFunc{	
			/**
			 *  Setup the FitFunction as Amp_g*exp(-((N-mu_g)/(2*sig_g))^2)+Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m))))
			 *  and the parameters in this order: Amp_g,mu_g,sig_g,Amp_m,mu_m,sig_m
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				String funcS = "Amp_g*exp(-1.0*(x[0]-mu_g)*(x[0]-mu_g)/(2*sig_g*sig_g))+Amp_m*(exp(-1.0*((x[0]-mu_m)/sig_m+exp(-1.0*(x[0]-mu_m)/sig_m))))";
				String parS = "Amp_g,mu_g,sig_g,Amp_m,mu_m,sig_m";
				IFunction func = ff.createFunctionFromScript("SpectrumFitFunc", 1, funcS, parS, "SpectrumFitFunc");
				func.setParameters(param);
				return func;
			}
		}
		return new GaussMoyalImp();
	}

	/**
	 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
	 *  ...+ Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m)))) and the parameters
	 *  in this order: Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...,Amp_m,mu_m,sig_m
	 *
	 *@return    SpectrumFitFunc
	 */
	public SpectrumFitFunc nGaussMoyal(){
		class NGaussMoyalImp implements SpectrumFitFunc{	
			/**
			 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
			 *  ...+ Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m)))) and the
			 *  parameters in this order: Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...,Amp_m,mu_m,sig_m
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 3) / 3;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("SpectrumFitFunc", 1, funcS, parS, "SpectrumFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					sbF.append("Amp_g" + i + "*exp(-0.5*pow((x[0]-mu_g" + i + ")/(sig_g" + i + "),2))+");
				}
				sbF.append("Amp_m*(exp(-1.0*((x[0]-mu_m)/sig_m+exp(-1.0*(x[0]-mu_m)/sig_m))))");
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != 0)
						sbP.append(",");
					sbP.append("Amp_g" + i + ",mu_g" + i + ",sig_g" + i);
				}
				sbP.append(",Amp_m,mu_m,sig_m");
				return sbP.toString();
			}
		}
		return new NGaussMoyalImp();
	}

	/**
	 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
	 *  ...+ Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m))))+Amp_e*exp(-N/N_e)
	 *  and the parameters in this order: Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...,Amp_m,mu_m,sig_m,Amp_e,N_e
	 *
	 *@return    SpectrumFitFunc
	 */
	public SpectrumFitFunc nGaussMoyalExp(){
		class NGaussMoyalExpImp implements SpectrumFitFunc{
			/**
			 *  Setup the FitFunction as Amp_g0*exp(-((N-mu_g0)/(2*sig_g0))^2)+Amp_g1*exp(-((N-mu_g1)/(2*sig_g1))^2)+
			 *  ...+ Amp_m*(exp(-((N-mu_m)/sig_m+exp(-(N-mu_m)/sig_m))))+Amp_e*exp(-N/N_e)
			 *  and the parameters in this order:
			 *  Amp_g0,mu_g0,sig_g0,Amp_g1,mu_g1,sig_g1,...,Amp_m,mu_m,sig_m,Amp_e,N_e
			 *
			 *@param  param  Description of the Parameter
			 *@return        Description of the Return Value
			 */
			public IFunction fitFunction(double[] param){
				int iter = (param.length - 5) / 3;
				String funcS = genFunc(iter);
				String parS = genParam(iter);
				IFunction func = ff.createFunctionFromScript("SpectrumFitFunc", 1, funcS, parS, "SpectrumFitFunc");
				func.setParameters(param);
				return func;
			}

			private String genFunc(int iter){
				StringBuilder sbF = new StringBuilder();
				for (int i = 0; i < iter; i++){
					sbF.append("Amp_g" + i + "*exp(-0.5*pow((x[0]-mu_g" + i + ")/(sig_g" + i + "),2))+");
				}
				sbF.append("Amp_m*(exp(-1.0*((x[0]-mu_m)/sig_m+exp(-1.0*(x[0]-mu_m)/sig_m))))");
				sbF.append("+Amp_e*exp(-1.0*x[0]/N_e)");
				return sbF.toString();
			}

			private String genParam(int iter){
				StringBuilder sbP = new StringBuilder();
				for (int i = 0; i < iter; i++){
					if (i != 0)
						sbP.append(",");
					sbP.append("Amp_g" + i + ",mu_g" + i + ",sig_g" + i);
				}
				sbP.append(",Amp_m,mu_m,sig_m");
				sbP.append(",Amp_e,N_e");
				return sbP.toString();
			}
		}
		return new NGaussMoyalExpImp();
	}
}
